﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using DXBase;
using System.Runtime.InteropServices;

namespace DXBase.Structs
{
	public struct SquareMatrixF
	{
		int size;
		float[] data;
		public SquareMatrixF(int n)
		{
			size = n;
			data = new float[n * n];
		}
		public int Size { get { return size; } }

		internal float[] GetDataArray() { return data; }

		public float this[int index]
		{
			get { return data[index]; }
			set { data[index] = value; }
		}

		public float this[int row, int col] 
		{
			get { return data[row * size + col]; }
			set { data[row * size + col] = value; }
		}

		public void Transpose()
		{
			for(int i=0; i<size-1; i++)
				for(int j=i+1; j<size; j++)
				{
					var tmp = this[i, j];
					this[i,j]  = this[j,i];
					this[j,i]  = tmp;
				}
		}

		public SquareMatrixF Minor(int i, int j)
		{
			if (size == 1)
				return this;

			SquareMatrixF result= new SquareMatrixF(size - 1);
			for (int iSrc = 0; iSrc < size; iSrc++)
				for (int jSrc = 0; jSrc < size; jSrc++)
				{
					if (iSrc == i) continue;
					if (jSrc == j) continue;
					int iDst = iSrc<i ? iSrc : iSrc-1;
					int jDst = jSrc<j ? jSrc : jSrc-1;
					result[iDst,jDst] = this[iSrc,jSrc];
				}
			return result;
		}
		public float Determinant()
		{
			if(size == 1)
				return data[0];
			float result = 0;
			for (int i=0; i<size; i++)
			{
				var minor = Minor(0, i);
				var det = minor.Determinant();
				result += (i%2==0 ? 1 : -1) * this[0,i] * det;
			}
			return result;
		}
		public SquareMatrixF Adjoint()
		{
			SquareMatrixF result = new SquareMatrixF(size);
			for (int i = 0; i < size; i++)
				for (int j = 0; j < size; j++)
				{
					var ij = i+j;
					result[i,j] = (ij % 2 == 0 ? 1 : -1) * Minor(i,j).Determinant();
				}
			return result;
		}
		public SquareMatrixF Invert()
		{
			SquareMatrixF result = new SquareMatrixF(size);
			float det = 0;
			for (int i=0; i<size; i++)
			{
				var minor = Minor(0, i);
				var num = (i % 2 == 0 ? 1 : -1) * minor.Determinant();
				det += this[0,i] * num;
				result[i,0] = num;
			}
			if (det.Absf() < 0.0000001f)
				return result;

			for (int i=0; i<size; i++)
				result[i,0] = result[i,0] / det;
			for (int i = 1; i < size; i++)
				for (int j = 0; j < size; j++)
				{
					var ij = i+j;
					result[j,i] = (ij % 2 == 0 ? 1 : -1) * Minor(i,j).Determinant() / det;
				}
			return result;
		}
		public static SquareMatrixF operator *(SquareMatrixF m1, SquareMatrixF m2)
		{
			if (m1.Size != m2.Size)
				throw new ArgumentOutOfRangeException();
			var res = new SquareMatrixF(m1.Size);
			for (int i = 0; i < m1.Size; i++)
				for (int j = 0; j < m1.Size; j++)
					for (int k = 0; k < m1.Size; k++)
						res[i, j] += m1[i, k] * m2[k, j];
			return res;
		}
	}
}
